﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Skyhook.AI.PathFinder
{
    static class PathFinderData
    {
        static int TargetX;
        static int TargetY;
        static int StartX;
        static int StartY;
        static int Width;
        static int Height;
        static int CurrentX;
        static int CurrentY;

        static PointDataSorter Sorter = new PointDataSorter();

        static PathNode[] NodesMap = null;
        static BlockingMap BlockingMap = null;
        static int pOpenedNodesCount = 0;
        static PointDataCache Cache = null;
        
        static int CalculateH(int x, int y)
        {
            return (Math.Abs(TargetX - x) + Math.Abs(TargetY - y)) * 2;
        }
        static int CalculateG(int parentg, int fromparentdirection)
        {
            if (fromparentdirection == MoveDirection.E || fromparentdirection == MoveDirection.N ||
                fromparentdirection == MoveDirection.S || fromparentdirection == MoveDirection.W)
            {
                return parentg + 3;
            }
            else
            {
                return parentg + 2;
            }
        }

        static public bool ProcessPoints()
        {
            PointData result = Sorter.GetMinPointData();
            if (result == null)
            {
                return false;
            }
            int x = result.Values.X;
            int y = result.Values.Y;

            int currentx;
            int currenty;
            int newg;
            for (int dir = MoveDirection.MinDirection; dir <= MoveDirection.MaxDirection; dir++)
            {
                currentx = x + MoveDirection.XShift[dir];
                currenty = y + MoveDirection.YShift[dir];

                if (currentx == TargetX && currenty == TargetY)
                {
                    AddOpenedNode(currentx, currenty, dir, NodesMap[y * Width + x].G, CalculateH(currentx, currenty));
                    return true;
                }

                if (currentx >= 0 && currentx < Width && currenty >= 0 && currenty < Height)
                {
                    if (NodesMap[currenty * Width + currentx].State == PathNodeStates.Open + PathNode.StateShift)
                    {
                        newg = CalculateG(NodesMap[y * Width + x].G, dir);
                        if (newg < NodesMap[currenty * Width + currentx].G)
                        {
                            ChangeOpenedNode(currentx, currenty, dir, NodesMap[y * Width + x].G, CalculateH(currentx, currenty));
                        }
                    }
                    if (NodesMap[currenty * Width + currentx].State <= PathNodeStates.None + PathNode.StateShift)
                    {
                        AddOpenedNode(currentx, currenty, dir, NodesMap[y * Width + x].G, CalculateH(currentx, currenty));
                    }
                }
            }

            RemoveOpenedNode(x, y);

            return false;
        }

        /*static PointData GetFirstOpenPoint()
        {
            PointData curpoint = FirstOpenedNode;
            PointData result = null;
            int minf = 1000000;
            for(; curpoint != null;curpoint = curpoint.Next)
            {
                if (curpoint.F < minf)
                {
                    minf = curpoint.F;
                    result = curpoint;
                }
            }
            return result;
        }*/
        static void AddOpenedNode(int x, int y, int direction, int g, int h)
        {
            NodesMap[y * Width + x].SetOpenedNode(direction, g, h);
            NodesMap[y * Width + x].Data = Cache.GetPointData(x, y, NodesMap[y * Width + x].F);
            Sorter.AddPointData(NodesMap[y * Width + x].Data);
            pOpenedNodesCount++;
        }
        
        static bool CheckPointData(PointData data)
        {
            return (data.Values.X == CurrentX && data.Values.Y == CurrentY);
        }
        static int ComparePointData(PointData p1, PointData p2)
        {
            return (p1.F - p2.F);
        }
        static void ChangeOpenedNode(int x, int y, int direction, int g, int h)
        {
            RemoveOpenedNode(x, y);
            AddOpenedNode(x, y, direction, g, h);
            //Sorter.RemovePointData(NodesMap[y * Width + x].Data);

            //int oldf = NodesMap[y * Width + x].Data.F;

            //NodesMap[y * Width + x].SetOpenedNode(direction, g, h);
           // NodesMap[y * Width + x].Data.F = NodesMap[y * Width + x].F;

            //Sorter.ChangePointData(NodesMap[y * Width + x].Data,oldf);

            //Sorter.AddPointData(NodesMap[y * Width + x].Data);
        }
        static void RemoveOpenedNode(int x, int y)
        {
            /*if (NodesMap[y * Width + x].Data.Values.Previous != null)
            {
                NodesMap[y * Width + x].Data.Values.Previous.Next = NodesMap[y * Width + x].Data.Next;
            }
            if (NodesMap[y * Width + x].Data.Next != null)
            {
                NodesMap[y * Width + x].Data.Next.Values.Previous = NodesMap[y * Width + x].Data.Values.Previous;
            }*/
            Sorter.RemovePointData(NodesMap[y * Width + x].Data);
            NodesMap[y * Width + x].SetClosedNode();
            pOpenedNodesCount--;
            Cache.RemovePointData(NodesMap[y * Width + x].Data);
        }
        public static void AddStartNode(int x, int y)
        {
            if (x >= 0 && x < Width && y >= 0 && y < Height)
            {
                if (NodesMap[y * Width + x].State <= PathNodeStates.None + PathNode.StateShift)
                {
                    StartX = x;
                    StartY = y;
                    AddOpenedNode(x, y, MoveDirection.Unknown, 0, CalculateH(x, y));
                }
            }
        }
        public static void AddFinishNode(int x, int y)
        {
            TargetX = x;
            TargetY = y;
        }

        public static void Initialize(BlockingMap map)
        {
            NodesMap = new PathNode[map.Width * map.Height];
            Width = map.Width;
            Height = map.Height;
            BlockingMap = map;

            BlockedPoint point = map.FirstBlockedPoint;
            while(point != null)
            {
                NodesMap[Width * point.Y + point.X] = new PathNode();
                NodesMap[Width * point.Y + point.X].SetPermanentlyClosedNode();
                point = point.Next;
            }
            Sorter.Clear();
            
            Cache = new PointDataCache(map.Width * map.Height);
        }
        public static void Clear()
        {
            PathNode.StateShift += 3;

            Sorter.Clear();

            Cache.Clear();
            pOpenedNodesCount = 0;
        }
        public static int OpenNodesCount
        {
            get
            {
                return pOpenedNodesCount;
            }
        }
        public static void GetFinalPath(MovePath path)
        {
            int x = TargetX;
            int y = TargetY;
            int newx = x;
            int newy = y;
            int startdir = NodesMap[Width * y + x].ParentDirection;
            int currentdir = NodesMap[Width * y + x].ParentDirection;

            while (x != StartX || y != StartY)
            {
                path.AddWaypointFirst(new Waypoint(x,y,currentdir));
                if (x != StartX || y != StartY)
                {
                    newx -= MoveDirection.XShift[NodesMap[Width * y + x].ParentDirection];
                    newy -= MoveDirection.YShift[NodesMap[Width * y + x].ParentDirection];
                    x = newx;
                    y = newy;
                    currentdir = NodesMap[Width * y + x].ParentDirection;
                }
            }
            return;
        }

    }
}
